package com.sghd.fight.module.game.core;

import com.sghd.common.utils.ManagedException;
import com.sghd.common.utils.json.JsonUtils;
import com.sghd.fight.module.game.core.model.ChessState;
import com.sghd.fight.module.game.core.model.EliminateResult;
import com.sghd.fight.utils.Random;
import com.sghd.pojo.fight.game.ChessType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

/**
 * @author frank
 * @date 2018-10-18 16:48
 * @desc
 **/
public class ChessBoard {
    private Logger logger = LoggerFactory.getLogger(getClass());
    /** 行*/
    private int row;
    /** 列*/
    private int col;
    /** 格子*/
    private Grid[] grids;
    /** 默认3个达成消除*/
    private int elimCount = 3;
    /** 随机函数*/
    private Random random;
    /**被消除的棋子*/
    private Set<Integer> eliminated = new HashSet<>();

    public ChessBoard(int row, int col, Random random) {
        this.row = row;
        this.col = col;
        grids = new Grid[row * col];
        for (int i = 0; i < grids.length; i++) {
            grids[i] = new Grid(i);
        }
        this.random = random;
    }

    public void setState(int s, int s1, int s2) {
        grids[s].getChess().addStatus(ChessState.LIGHTNING3);
        grids[s].getChess().setType(ChessType.CT_LIGHTNING);
        grids[s1].getChess().addStatus(ChessState.LIGHTNING3);
        grids[s1].getChess().setType(ChessType.CT_LIGHTNING);
        grids[s2].getChess().addStatus(ChessState.LIGHTNING3);
        grids[s2].getChess().setType(ChessType.CT_LIGHTNING);

    }

    public Random getRandom() {
        return random;
    }

    public void setRandom(Random random) {
        this.random = random;
    }

    public static void main(String[] args) {
        String text = "绿\t黄\t紫\t红\t红\t绿\t红\t红\t\n" +
                "蓝\t绿\t电\t蓝\t黄\t蓝\t绿\t黄\t\n" +
                "绿\t红\t红\t黄\t电\t黄\t电\t电\t\n" +
                "绿\t紫\t绿\t电\t绿\t黄\t绿\t绿\t\n" +
                "红\t黄\t黄\t紫\t绿\t绿\t蓝\t绿\t\n" +
                "紫\t蓝\t电\t红\t电\t红\t电\t蓝\t\n" +
                "绿\t黄\t黄\t蓝\t紫\t黄\t红\t蓝\t\n" +
                "红\t蓝\t红\t绿\t绿\t电\t电\t紫";
        String[] textS = text.split("\n");
        String[] chess = new String[64];

        for (int i = 0; i < textS.length; i++) {
            String[] col = textS[i].split("\t");
            for (int k = 0; k < col.length; k++) {
                chess[k * 8 + i] = col[k];
            }
        }
        ChessBoard chessBoard = new ChessBoard(8, 8, new Random(89744911789397L));
//        chessBoard.genarete();
        for (int i = 0; i < chess.length; i++) {
            Chess chess1 = new Chess(ChessType.valueOfShow(chess[i]));
            chessBoard.grids[i].setChess(chess1);
        }
        chessBoard.print("生成后的棋盘！");

        System.out.println(chessBoard.canElim(false));
        System.out.println("seed" + chessBoard.random.getSeed());
        Scanner scanner = new Scanner(System.in);
//        int s = scanner.nextInt();
//        int s1 = scanner.nextInt();
//        int s2 = scanner.nextInt();
//        chessBoard.setState(s, s1, s2);
//        chessBoard.print("设置完毕后的棋盘");
        int i = 5;
        while (i > 0) {
            int one = scanner.nextInt();
            int two = scanner.nextInt();
            chessBoard.swap(one, two, false);
            i--;
        }
        long start = System.nanoTime();
        System.out.println("耗时：" + (System.nanoTime() - start));
    }

    /**
     * 生成棋盘
     * @param random
     */
    public void genarete() {
        for (int i = 0; i < grids.length; i++) {
            Grid grid = grids[i];
            if (!grid.isEmpty()) {
                continue;
            }
            Chess chess = getChess(i, random, true);
            grids[i].setChess(chess);
        }
        print("生成后的棋盘！");
        logger.debug("生成后的棋盘随机种子 : {}", random.getSeed());
    }

    public void reset() {
        clear();
        genarete();
    }

    public void clear() {
        for (Grid grid : grids) {
            grid.remove();
        }
    }

    /**
     * 生成棋盘数据
     * @param index
     * @param random
     * @param init
     * @return
     */
    private Chess getChess(int index, Random random, boolean init) {
        ChessType type = randomChess(random);
        Chess chess = new Chess(type);
        if (!init) {
            return chess;
        }
        if (!canEliminate(index, type)) {
            //不会自动消除
            return chess;
        }
        return getChess(index, random, init);
    }

    /**
     * 随机棋子
     * @param random
     * @return
     */
    public static ChessType randomChess(Random random) {
        int ordinal = random.nextInt(ChessType.values().length);
        ChessType type = ChessType.values()[ordinal];
        return type;
    }

    /**
     * 判断是否存在可自消除的棋子
     * @param index
     * @param type
     * @return true存在 false不存在
     */
    private boolean canEliminate(int index, ChessType type) {

        if (getRowElim(index, type).size() >= elimCount) {
            return true;
        }
        if (getColElim(index, type).size() >= elimCount) {
            return true;
        }
        return false;
    }

    public Set<Integer> boom(int index) {
        Set<Integer> set = new HashSet<>();
        int i = index % row;  //取横
        int j = index / col;  //取列
        boolean isTop = i == 0;
        boolean isBot = i == 7;
        boolean isHead = j == 0;
        boolean isTail = j == 7;
        if (!isTop) {
            set.add(index - 1);
        }
        if (!isHead) {
            set.add(index - row);
        }
        if (!isBot) {
            set.add(index + 1);
        }
        if (!isTail) {
            set.add(index + row);
        }
        if (!isHead && !isTop) {
            set.add(index - row - 1);
        }
        if (!isHead && !isBot) {
            set.add(index - row + 1);
        }
        if (!isTail && !isTop) {
            set.add(index + row - 1);
        }
        if (!isTail && !isBot) {
            set.add(index + row + 1);
        }
        set.add(index);
        return set;
    }

    /**
     * 获取横向可消除的元素索引
     * @param index
     * @param type
     * @return
     */
    private Set<Integer> getRowElim(int index, ChessType type) {
        Set<Integer> result = new HashSet<>();
        int rowMini = index % row;
        int rowStart = index - (elimCount - 1) * row;
        rowStart = Math.max(rowStart, rowMini);

        int rowMax = rowMini + row * (col - 1);
        int rowEnd = index + (elimCount - 1) * row;
        rowEnd = Math.min(rowMax, rowEnd);
        for (int i = rowStart; i <= rowEnd; i += row) {
            if (i == index) {
                result.add(i);
                continue;
            }
            Grid chess = grids[i];
            if (chess == null || chess.isEmpty()) {
                break;
            }
            if (chess.getType() == type) {
                result.add(i);
            } else if (result.size() >= elimCount) {
                break;
            } else {
                result.clear();
            }
        }
        if (result.size() < elimCount) {
            result.clear();
        }
        return result;
    }

    /**
     * 获取纵向可消除的元素
     * @param index
     * @param type
     * @return
     */
    private Set<Integer> getColElim(int index, ChessType type) {
        Set<Integer> result = new HashSet<>();
        int curCol = index / row;
        int colMini = curCol * row;
        int colStart = index - elimCount + 1;
        colStart = Math.max(colStart, colMini);

        int colMax = colMini + row - 1;
        int colEnd = index + elimCount - 1;
        colEnd = Math.min(colMax, colEnd);
        for (int i = colStart; i <= colEnd; i += 1) {
            if (i == index) {
                result.add(i);
                continue;
            }
            Grid chess = grids[i];
            if (chess == null || chess.isEmpty()) {
                break;
            }
            if (chess.getType() == type) {
                result.add(i);
            } else if (result.size() >= elimCount) {
                break;
            } else {
                result.clear();
            }
        }
        if (result.size() < elimCount) {
            result.clear();
        }
        return result;
    }

    /**
     * 交换棋子
     * @param first
     * @param second
     * @param fighter
     */
    public EliminateResult swap(int first, int second, boolean tool) {
        if (first == second) {
            throw new ManagedException(-1, "交换棋子下标有误！");
        }
        int min = Math.min(first, second);
        int max = Math.max(first, second);
        if (!tool) {
            if (min + 1 != max && min + row != max) {
                throw new ManagedException(-1, "交换棋子下标有误！");
            }
            if (max % row == 0 && max - row != min) {
                throw new ManagedException(-1, "交换棋子下标有误！");
            }
            if ((min + 1) % row == 0 && max - row != min) {
                throw new ManagedException(-1, "交换棋子下标有误！");
            }
        }
        //返回值
//        List<EliminateGroup> result = new ArrayList<>();
        Grid fstGrid = grids[first];
        Grid scdGrid = grids[second];
        //交换
        fstGrid.swap(scdGrid);
        ChessType fstType = scdGrid.getType();
        ChessType scdType = fstGrid.getType();
        //寻找可以消除的棋子
        Set<Integer> fstElim = getElimChess(second, fstType);
        Set<Integer> scdElim = getElimChess(first, scdType);
        //数量无法达到消除数量
        if (fstElim.size() < elimCount && scdElim.size() < elimCount) {
            fstGrid.swap(scdGrid);//交换回来
            throw new ManagedException(-1, "无法完成消除！");
        }
        EliminateResult eResult = new EliminateResult();

//        EliminateGroup fstGroup = new EliminateGroup();
//        result.add(fstGroup);
//
//        if (fstElim.isEmpty()) {
//            fstGroup.type = scdType;
//            fstGroup.groups.addAll(scdElim);
//        } else {
//            fstGroup.type = fstType;
//            fstGroup.groups.addAll(fstElim);
//            if (scdElim.size() > 0) {
//                EliminateGroup scdGroup = new EliminateGroup();
//                scdGroup.type = scdType;
//                scdGroup.groups.addAll(scdElim);
//                result.add(scdGroup);
//            }
//        }
        //收集可消除元素放到返回值
        Set<Set<Integer>> elimtByDown = new HashSet<>();

        Set<Set<Chess>> elimtByChess = new HashSet<>();


        if (fstElim != null && fstElim.size() != 0) {
            eResult.getSets().add(fstElim);
            Set<Chess> chessTypes = new HashSet<>();
            for (Integer fstIdx : fstElim) {
                chessTypes.add(grids[fstIdx].getChess());
            }
            elimtByChess.add(chessTypes);
            elimtByDown.add(fstElim);
        }
        if (scdElim != null && scdElim.size() != 0) {
            eResult.getSets().add(scdElim);
            Set<Chess> chessTypes = new HashSet<>();
            for (Integer scdIdx : scdElim) {
                chessTypes.add(grids[scdIdx].getChess());
            }
            elimtByChess.add(chessTypes);
            elimtByDown.add(scdElim);
        }
        if (logger.isDebugEnabled()) {
            logger.debug("第一次交换消除:{}", JsonUtils.object2String(elimtByDown));
        }
        eResult.getElimtByDown().add(elimtByDown);//分组记录每次下落的组合
        eResult.getElimitByChess().add(elimtByChess);//分组记录每次下落的组合
        Collection<Integer> upgrades = eliminate(eResult, fstElim, scdElim);
        print("消除后的棋盘");
        logger.debug("消除后的棋盘随机种子 :{} ", random.getSeed());
        if (upgrades.size() >= 4) {
            upgrade(upgrades, first, second);
        }
        do {
            fill();
        } while (autoElim1(eResult));

        logger.debug(eResult.toString());
        return eResult;
    }

    /**
     * 消除棋子
     * @param all
     * EliminateResult eResult
     */
    public Collection<Integer> eliminate(EliminateResult eResult, Collection<Integer>... alls) {
        Collection<Integer> upgrade = new HashSet<>();
        Set<Integer> set = new HashSet<>();
        if (alls.length != 0 && alls != null) {
            for (int i = 0; i < alls.length; i++) {
                for (Integer index : alls[i]) {
                    Grid grid = grids[index];
                    //TODO 收集
                    if (grid.getChess() != null) {
                        eResult.getList().add(grid.getChess());
                        if (grid.getType() == ChessType.CT_LIGHTNING) {
                            upgrade.add(index);
                            if (grid.getChess().hasStatus(ChessState.LIGHTNING3)) {
                                grid.remove();
                                set.addAll(boom(index));
//                                eResult.getSetBoom().add(set);
                            } else {
                                grid.remove();
                            }
                        } else {
                            grid.remove();
                        }
                    }
                }
                eliminated.addAll(alls[i]);
            }
        }
        if (set.size() != 0) {
            logger.debug("触发爆炸");
            eliminate(eResult, set);
            print("触发爆炸后的棋盘");
        }
        return upgrade;
    }

    /**
     * 闪电元素的升级方法
     * @param upgrade 可以升级的闪电元素对象
     * @param first 第一个元素下标
     * @param second 第二个元素下标
     */
    public void upgrade(Collection<Integer> upgrade, int first, int second) {
        int index = -1;
        int size = upgrade.size();
        In:
        for (Integer i : upgrade) {
            if (i == first) {
                index = first;
                break In;
            } else if (i == second) {
                index = second;
                break In;
            } else {
                index = i;
            }
        }
        Chess chess = new Chess(ChessType.CT_LIGHTNING);
        if (size == 4) {
            chess.addStatus(ChessState.LIGHTNING2);
        } else if (size >= 5) {
            chess.addStatus(ChessState.LIGHTNING3);
        }
        grids[index].setChess(chess);
        eliminated.remove(index);
    }

    /**
     * 自动消除的时候  调用的闪电元素的升级方法
     * @param upgrade
     */
    public void upgrade(Collection<Integer> upgrade) {
        int size = upgrade.size();
        List<Integer> list = new ArrayList<>(upgrade);
        Collections.sort(list);
        Chess chess = new Chess(ChessType.CT_LIGHTNING);
        if (size == 4) {
            chess.addStatus(ChessState.LIGHTNING2);
        } else if (size >= 5) {
            chess.addStatus(ChessState.LIGHTNING3);
        }
        grids[list.get(0)].setChess(chess);
        eliminated.remove(list.get(0));
    }

    /**
     * 获取当前元素头顶的元素
     * @param currentIndex
     * @param random
     * @return
     */
    private Chess getTop(int currentIndex, Random random) {
        //是否第一行
        boolean firstRow = currentIndex % row == 0;
        Chess result = null;
        if (firstRow) {
            //result = getChess(currentIndex, random, false);
            return null;
        } else {
            int topIndex = currentIndex - 1;
            Grid chess = grids[topIndex];
            if (chess.isEmpty()) {
                result = getTop(topIndex, random);
            } else {
                result = chess.remove();
            }
            chess.fill(getTop(topIndex, random));
        }
        return result;
    }

    /**
     * 下落并填充棋子
     * @param random
     */
    public void fill() {
        //下落
        List<Integer> sorted = new ArrayList<>(eliminated);
        Collections.sort(sorted);
        int size = sorted.size();
        for (; size > 0; size--) {
            int index = sorted.get(size - 1);
            Grid grid = grids[index];
            if (grid.isEmpty()) {
                grid.fill(getTop(index, random));
            }
        }
        print("下落后的棋盘！");
        logger.debug("下落后的棋盘随机种子 : {}", random.getSeed());
        //填充
        Arrays.stream(grids).filter(grid -> grid.isEmpty()).forEach(grid -> {
            Chess chess = getChess(grid.getIndex(), random, false);
            grid.fill(chess);
        });
        eliminated.clear();
        print("填充后的棋盘！");
        logger.debug("填充后的棋盘随机种子 :{} ", random.getSeed());
        //print();
    }

    /**
     * 自动消除棋盘
     */
//    public boolean autoEmil() {
//        boolean flag = false;
//        Set<Integer> autos = new HashSet<>();
//        for (Grid grid : grids) {
//            if (autos.contains(grid.getIndex())) {
//                continue;
//            }
//            //寻找可消除的棋子
//            Set<Integer> elims = getColElim(grid.getIndex(), grid.getType());
//            //横向可以消除的棋子
//            elims.addAll(getRowElim(grid.getIndex(), grid.getType()));
//            autos.addAll(elims);
//        }
//        eliminate(autos);
//        print("自动消除后的棋子");
//        return flag;
//    }

    /**
     * 获取消除的棋子
     * @param index
     * @param type
     * @return
     */
    public Set<Integer> getElimChess(int index, ChessType type) {
        //先横排 后竖列
        Set<Integer> result = new HashSet<>();
        result.addAll(getRowElim(index, type));
        result.addAll(getColElim(index, type));
        return result;
    }

    public static class EliminateGroup {
        public ChessType type;
        public Collection<Integer> groups = new HashSet<>();
    }

    /**
     * 打印棋盘
     */
    public void print(String msg) {
        if (!logger.isDebugEnabled()) {
            return;
        }
        System.out.println(msg);
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                int index = i + j * row;
                Grid grid = grids[index];
                Chess chess = grid.getChess();
                String type = "Nn";
                if (chess != null && !chess.isEmpty()) {
                    type = chess.getType().getShow();
                    if (chess.getType() == ChessType.CT_LIGHTNING && chess.hasStatus(ChessState.LIGHTNING2)) {
                        type = type + "2";
                    }
                    if (chess.getType() == ChessType.CT_LIGHTNING && chess.hasStatus(ChessState.LIGHTNING3)) {
                        type = type + "3";
                    }
                }
                System.out.print(type + "\t");
            }
            System.out.println("\n");
        }
    }

    /**
     * 检查棋盘是否可以通过交换棋子进行消除(死棋盘)
     * @return
     */
    public List<List<Integer>> canElim(Boolean all) {
        List<List<Integer>> elims = new ArrayList<>();
        /***/
        Set<Integer> outed = new HashSet<>();
        for (Grid grid : grids) {
            List<Integer> swaps = up(grid, outed);
            if (swaps != null) {
                elims.add(swaps);
                if (!all) {
                    return elims;
                }
            }
            swaps = down(grid, outed);
            if (swaps != null) {
                elims.add(swaps);
                if (!all) {
                    return elims;
                }
            }
            swaps = left(grid, outed);
            if (swaps != null) {
                elims.add(swaps);
                if (!all) {
                    return elims;
                }
            }
            swaps = right(grid, outed);
            if (swaps != null) {
                elims.add(swaps);
                if (!all) {
                    return elims;
                }
            }
        }
        return elims;
    }

    /**
     * 新增的自动消除算法
     * @return
     */
    public boolean autoElim1(EliminateResult eResult) {
        boolean flag = false;
        Set<Integer> elim = new HashSet<>();
        Set<Set<Integer>> elimByGroup = new HashSet<>();
        Set<Set<Chess>> elimByType = new HashSet<>();
        for (Grid grid : grids) {
            if (elim.contains(grid.getIndex())) {
                continue;
            }
            Set<Integer> elims = getE(grid, new HashSet<>());
            if (!elims.isEmpty()) {
                logger.debug(elims.toString());
                flag = true;
                elimByGroup.add(elims);
                elim.addAll(elims);

                Set<Chess> chessTypes = new HashSet<>();
                for (Integer idx : elims) {
                    chessTypes.add(grids[idx].getChess());
                }
                elimByType.add(chessTypes);
            }
        }
        if (logger.isDebugEnabled() && !elimByGroup.isEmpty()) {
            logger.debug("下落消除:{}", JsonUtils.object2String(elimByGroup));
        }
        //记录本次自动消除的组合
        eResult.getElimtByDown().add(elimByGroup);
        //记录本次自动消除的组合
        eResult.getElimitByChess().add(elimByType);
        elimByGroup.forEach(elims -> {
            if (elims.size() != 0) {
                eResult.getSets().add(elims);
            }
            Collection<Integer> upgrades = eliminate(eResult, elims);
            if (upgrades.size() >= 4) {
                upgrade(upgrades);
            }
        });
        return flag;
    }

    private Set<Integer> getE(Grid grid, Set<Integer> outed) {
        Set<Integer> result = getElimChess(grid.getIndex(), grid.getType());
        outed.add(grid.getIndex());
        for (Integer index : new HashSet<>(result)) {
            if (outed.contains(index)) {
                continue;
            }
            result.addAll(getE(grids[index], outed));
        }
        return result;
    }

    private List<Integer> up(Grid source, Set<Integer> outs) {
        if (source.getIndex() % row == 0) {//已经是第一行
            return null;
        }
        Grid target = grids[source.getIndex() - 1];//正上方相临的元素
        if (target.getType() == source.getType()) {
            return null;
        }
        if (outs.contains(source.getIndex() + target.getIndex())) {
            return null;
        }
        outs.add(source.getIndex() + target.getIndex());
        return getSwaps(source, target);
    }

    private List<Integer> down(Grid source, Set<Integer> outs) {
        if ((source.getIndex() + 1) % row == 0) {//已经是最下方一行
            return null;
        }
        Grid target = grids[source.getIndex() + 1];//正下方相临的元素
        if (target.getType() == source.getType()) {
            return null;
        }
        if (outs.contains(source.getIndex() + target.getIndex())) {
            return null;
        }
        outs.add(source.getIndex() + target.getIndex());
        return getSwaps(source, target);
    }

    private List<Integer> left(Grid source, Set<Integer> outs) {
        if (source.getIndex() / row == 0) {//已经是最左边
            return null;
        }
        Grid target = grids[source.getIndex() - row];//左边相临的元素
        if (target.getType() == source.getType()) {
            return null;
        }
        if (outs.contains(source.getIndex() + target.getIndex())) {
            return null;
        }
        outs.add(source.getIndex() + target.getIndex());
        return getSwaps(source, target);
    }

    private List<Integer> right(Grid source, Set<Integer> outs) {
        if ((source.getIndex() / row) + 1 == col) {//已经是最右边
            return null;
        }
        Grid target = grids[source.getIndex() + row];//右边相临的元素
        if (target.getType() == source.getType()) {
            return null;
        }
        if (outs.contains(source.getIndex() + target.getIndex())) {
            return null;
        }
        outs.add(source.getIndex() + target.getIndex());
        return getSwaps(source, target);
    }

    private List<Integer> getSwaps(Grid source, Grid target) {
        source.swap(target);
        List<Integer> results = new ArrayList<>();
        //找出第一元素可消除的棋子
        results.addAll(getColElim(source.getIndex(), source.getType()));
        results.addAll(getRowElim(source.getIndex(), source.getType()));
        if (!results.isEmpty()) {
            source.swap(target);//交换回来
            return Arrays.asList(source.getIndex(), target.getIndex());
        } else {
            //第二元素可消除的棋子
            results.addAll(getColElim(target.getIndex(), target.getType()));
            results.addAll(getRowElim(target.getIndex(), target.getType()));
            source.swap(target);//交换回来
            if (!results.isEmpty()) {
                return Arrays.asList(source.getIndex(), target.getIndex());
            }
            return null;
        }
    }

    public Set<Integer> getEliminated() {
        return eliminated;
    }

    public ChessBoard copy() {
        ChessBoard copyOf = new ChessBoard(row, col, new Random(random.getSeed()));
        for (int i = 0; i < grids.length; i++) {
            copyOf.grids[i] = this.grids[i].copy();
        }
        copyOf.eliminated = new HashSet<>(this.eliminated);
        return copyOf;
    }

    public int getRow() {
        return row;
    }

    public int getCol() {
        return col;
    }

    public Grid[] getGrids() {
        return grids;
    }
}
